package com.hzxy.yiyid.web.tool;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class HttpClientTool {

    private static final Logger log = LoggerFactory.getLogger(HttpClientTool.class);
    // httpClient连接超时时间
    private static int HTTP_CLIENT_CONNECTION_TIMEOUT = 10000;
    private static int HTTP_CLIENT_SO_TIMEOUT = 30000;
    private static int bufferSize = 1024;
    private static volatile HttpClientTool instance;
    private ConnectionConfig connConfig;
    private SocketConfig socketConfig;
    private ConnectionSocketFactory plainSF;
    private SSLContext sslContext;
    private LayeredConnectionSocketFactory sslSF;
    private Registry<ConnectionSocketFactory> registry;
    private PoolingHttpClientConnectionManager connManager;
    private volatile HttpClient client;
    private volatile BasicCookieStore cookieStore;
    public static String defaultEncoding = "utf-8";
    private static final ThreadLocal<HttpRequestBase> threadLocal = new ThreadLocal<HttpRequestBase>();

    private HttpClientTool() {
        // 设置连接参数
        connConfig = ConnectionConfig.custom().setCharset(Charset.forName(defaultEncoding)).build();
        socketConfig = SocketConfig.custom().setSoTimeout(HTTP_CLIENT_SO_TIMEOUT).build();
        RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create();
        plainSF = new PlainConnectionSocketFactory();
        registryBuilder.register("http", plainSF);
        // 指定信任密钥存储对象和连接套接字工厂
        try {
            sslContext = SSLContexts.custom().loadTrustMaterial(new TrustStrategy() {

                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            sslSF = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
            registryBuilder.register("https", sslSF);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        registry = registryBuilder.build();
        // 设置连接管理器
        connManager = new PoolingHttpClientConnectionManager(registry);
        connManager.setDefaultConnectionConfig(connConfig);
        connManager.setDefaultSocketConfig(socketConfig);
        // 指定cookie存储对象
        cookieStore = new BasicCookieStore();
        // 构建客户端
        client = HttpClients.custom().setDefaultCookieStore(cookieStore).setRedirectStrategy(new LaxRedirectStrategy()).setConnectionManager(connManager).build();
    }

    /**
     * 单例
     *
     * @return
     */
    public static HttpClientTool getInstance() {
        synchronized (HttpClientTool.class) {
            if (HttpClientTool.instance == null) {
                instance = new HttpClientTool();
            }
            return instance;
        }
    }

    private static List<NameValuePair> paramsConverter(Map<String, String> params) {
        List<NameValuePair> nvps = new LinkedList<NameValuePair>();
        Set<Entry<String, String>> paramsSet = params.entrySet();
        for (Entry<String, String> paramEntry : paramsSet) {
            nvps.add(new BasicNameValuePair(paramEntry.getKey(), paramEntry.getValue()));
        }
        return nvps;
    }

    /*
     * @Author RAICOM2023
     * @Description //读取内容
     * @Date 17:26 2023/5/1
     * @Param [in, encoding]
     * @return java.lang.String
     **/
    public static String readStream(InputStream in, String encoding) {
        if (in == null) {
            return null;
        }
        try {
            InputStreamReader inReader = null;
            if (encoding == null) {
                inReader = new InputStreamReader(in, defaultEncoding);
            } else {
                inReader = new InputStreamReader(in, encoding);
            }
            char[] buffer = new char[bufferSize];
            int readLen = 0;
            StringBuffer sb = new StringBuffer();
            while ((readLen = inReader.read(buffer)) != -1) {
                sb.append(buffer, 0, readLen);
            }
            inReader.close();
            return sb.toString();
        } catch (IOException e) {
            log.error("读取返回内容出错", e);
        }
        return null;
    }

    /*
     * @Author RAICOM2023
     * @Description //基本的Get请求
     * @Date 17:27 2023/5/1
     * @Param [url, queryParams]
     * @return org.apache.http.HttpResponse
     **/
    public HttpResponse doGet(String url, Map<String, String> queryParams) throws URISyntaxException, ClientProtocolException, IOException {
        HttpGet gm = new HttpGet();
        threadLocal.set(gm);
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(HTTP_CLIENT_CONNECTION_TIMEOUT).build();
        gm.setConfig(requestConfig);
        URIBuilder builder = new URIBuilder(url);
        // 填入查询参数
        if (queryParams != null && !queryParams.isEmpty()) {
            builder.setParameters(HttpClientTool.paramsConverter(queryParams));
        }
        gm.setURI(builder.build());
        HttpResponse httpResponse = client.execute(gm);
        return httpResponse;
    }

    /*
     * @Author RAICOM2023
     * @Description //get请求
     * @Date 17:27 2023/5/1
     * @Param [url, queryParams]
     * @return java.io.InputStream
     **/
    public InputStream doGetForStream(String url, Map<String, String> queryParams) throws UnsupportedOperationException, IOException, URISyntaxException {
        HttpResponse response = this.doGet(url, queryParams);
        return response != null ? response.getEntity().getContent() : null;
    }

    /*
     * @Author RAICOM2023
     * @Description //get请求
     * @Date 17:27 2023/5/1
     * @Param [url, queryParams]
     * @return java.lang.String
     **/
    public String doGetForString(String url, Map<String, String> queryParams) throws UnsupportedOperationException, IOException, URISyntaxException {
        return HttpClientTool.readStream(this.doGetForStream(url, queryParams), null);
    }

    /*
     * @Author RAICOM2023
     * @Description //基本的Post请求
     * @Date 17:27 2023/5/1
     * @Param [url, queryParams, formParams, jsonParams]
     * @return org.apache.http.HttpResponse
     **/
    public HttpResponse doPost(String url, Map<String, String> queryParams, Map<String, String> formParams, Map<String, String> jsonParams) throws URISyntaxException, ClientProtocolException, IOException {
        HttpPost pm = new HttpPost();
        threadLocal.set(pm);
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(HTTP_CLIENT_CONNECTION_TIMEOUT).build();
        pm.setConfig(requestConfig);
        URIBuilder builder = new URIBuilder(url);
        // 填入查询参数
        if (queryParams != null && !queryParams.isEmpty()) {
            builder.setParameters(HttpClientTool.paramsConverter(queryParams));
        }
        pm.setURI(builder.build());
        // 填入表单参数
        if (formParams != null && !formParams.isEmpty()) {
            pm.setEntity(new UrlEncodedFormEntity(HttpClientTool.paramsConverter(formParams)));
        }
        if (jsonParams != null && !jsonParams.isEmpty()) {
            StringEntity entity = new StringEntity(JacksonTool.bean2Json(jsonParams), defaultEncoding);
            entity.setContentEncoding(defaultEncoding);
            entity.setContentType("application/json");
            pm.setEntity(entity);
        }
        HttpResponse httpResponse = client.execute(pm);
        return httpResponse;
    }

    /*
     * @Author RAICOM2023
     * @Description //post请求
     * @Date 17:27 2023/5/1
     * @Param [url, queryParams, formParams, jsonParams]
     * @return java.io.InputStream
     **/
    public InputStream doPostForStream(String url, Map<String, String> queryParams, Map<String, String> formParams, Map<String, String> jsonParams) throws ClientProtocolException, URISyntaxException, IOException {
        HttpResponse response = this.doPost(url, queryParams, formParams, jsonParams);
        return response != null ? response.getEntity().getContent() : null;
    }

    /*
     * @Author RAICOM2023
     * @Description //post请求
     * @Date 17:27 2023/5/1
     * @Param [url, queryParams, formParams, jsonParams]
     * @return java.lang.String
     **/
    public String doPostForString(String url, Map<String, String> queryParams, Map<String, String> formParams, Map<String, String> jsonParams) throws ClientProtocolException, URISyntaxException, IOException {
        return HttpClientTool.readStream(this.doPostForStream(url, queryParams, formParams, jsonParams), null);
    }

    /*
     * @Author RAICOM2023
     * @Description //Post请求
     * @Date 17:27 2023/5/1
     * @Param [url, obj]
     * @return java.lang.String
     **/
    public String doPost(String url, Object obj) throws ClientProtocolException, URISyntaxException, IOException {
        HttpPost pm = new HttpPost();
        threadLocal.set(pm);
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(HTTP_CLIENT_CONNECTION_TIMEOUT).build();
        pm.setConfig(requestConfig);
        URIBuilder builder = new URIBuilder(url);
        pm.setURI(builder.build());
        String json;
        if (null == obj) {
            return null;
        } else if (obj instanceof String) {
            json = obj.toString();
        } else {
            json = JacksonTool.bean2Json(obj);
        }
        StringEntity entity = new StringEntity(json, defaultEncoding);
        entity.setContentEncoding(defaultEncoding);
        entity.setContentType("application/json");
        pm.setEntity(entity);
        HttpResponse response = client.execute(pm);
        InputStream in = response != null ? response.getEntity().getContent() : null;
        return HttpClientTool.readStream(in, null);
    }

    public void releaseConnection() {
        threadLocal.get().releaseConnection();
        threadLocal.remove();
    }
}
